import os
import sys
import codecs
from PyQt4.QtCore import *
from PyQt4.QtGui import *
#from PyQt4.QtOpenGL import *

import numpy.random as ra
import qimage2ndarray as qn
from browse3 import *
from QtPixelLabel import BoxItem, CritPointsItem


class InteractiveImage(QGraphicsView):
    def __init__(self):
        QGraphicsView.__init__(self)
        self.points = []
    def mouseReleaseEvent(self,ev):
        #print ev.pos()
        ctrl = False
        shift = False
        if( hasattr(ev,'modifiers') ):
            #print "has modifiers"
            if( ev.modifiers() & Qt.ShiftModifier ):
                shift = True
            if( ev.modifiers() & Qt.ControlModifier ):    
                ctrl = True

        
        pos = self.mapToScene(ev.pos())
        #print ctrl,shift,ev.pos(),pos,self.mapFromScene(pos)
        self.emit(SIGNAL("pointSelect"),{'pos':pos,'shift':shift,'ctrl':ctrl})
class browseqt(browse,QMainWindow):
    def __init__(self,win_parent,**kwargs):
        QMainWindow.__init__(self, win_parent)
        self.AllowModifyPoints = kwargs.pop('AllowModifyPoints',False) 
        self.AllowAddPoints = kwargs.pop('AllowAddPoints',False)
        self.AllowDeletePoints = kwargs.pop('AllowDeletePoints',False)
        self.commentsFile = kwargs.pop('commentsFile','comments.txt')
        self.report = kwargs.pop( "ReportText",None )
        self.winlevs = kwargs.pop("winlevs",{})
        
        
        browse.__init__(self,**kwargs)
        
        self.vi.winlevelimage()
        try:
            from . bqt_instructions import instructions
            self.instructions = instructions
        except:
            self.instructions = None
        self.create_widgets()
        self.createMenues()
        self.doLayout()
    def create_widgets(self):
        #Widgets
        self.winLabel = QLabel("win")
        self.windowCtrl= QSpinBox()
        self.windowCtrl.setSuffix("win")
        winlevSettings = self.vi.getColorSettings()
        self.windowCtrl.setRange(winlevSettings['wl'],winlevSettings['wh'])
        self.connect(self.windowCtrl,
                     SIGNAL("valueChanged(int)"),self.changeWindow)
        self.levLabel = QLabel("lev")
        self.levelCtrl = QSpinBox()
        self.levelCtrl.setSuffix("lev")
        self.levelCtrl.setRange(winlevSettings['ll'],winlevSettings['lh'])

        self.connect(self.levelCtrl,
                     SIGNAL("valueChanged(int)"),self.changeLevel)
        if( self.vi.mode == 3 ):
            self.thickCtrl = QSpinBox()
            self.thickLabel = QLabel("proj. thick")
            self.thickCtrl.setSuffix("thick")
            self.connect(self.thickCtrl,
                              SIGNAL("valueChanged(int)"),self.changeThick)

            self.sliceCtrl = QSpinBox()
            self.sliceLabel = QLabel("slice")
            self.connect(self.sliceCtrl,
                             SIGNAL("valueChanged(int)"),self.changeSlice)
            self.UpdateGeometryDefinitions()
        self.imageReport = QTabWidget()
        self.grview = InteractiveImage()
        #self.grview.setViewport(QGLWidget())
        self.grview.setViewport(QWidget())
        self.connect(self.grview,SIGNAL("pointSelect"),self.processImageClick)
        self.scene = QGraphicsScene()
        self.grview.setScene(self.scene)
        #self.scene.addPixmap(QPixmap('DSC_2201.jpg'))
        self.init_buffer()
        #self.scene.addPixmap(QPixmap('DSC_2201.jpg'))
        self.grview.setScene(self.scene)
        self.imageReport.addTab(self.grview,"Image")

        if( self.report ):
            self.reportView = QTextEdit()
            self.reportView.setReadOnly(True)
            self.reportView.setText(self.report)
            self.imageReport.addTab(self.reportView,"Report")
        self.commentEditor = QTextEdit()
        self.imageReport.addTab( self.commentEditor, "Comments" )
        if( self.instructions ):
            self.instructionsView = QTextEdit()
            self.instructionsView.setReadOnly(True)
            self.instructionsView.setHtml(self.instructions)
            self.imageReport.addTab( self.instructionsView, "Instructions")
        self.grview.show()


    def createMenues(self):
        self.createAnnoMenu()
        self.createOrientMenu()
        self.createScaleMenu()
        if( self.vi.mode == 3 ):
            self.createViewMenu()
            self.createGlobalLocalMenu()
            self.createProjectionMenu()
        if( self.winlevs ):
            self.createWinLevMenu()
    def doLayout3D(self):
        sliceBox = QHBoxLayout()
        sliceBox.addWidget(self.sliceLabel)
        sliceBox.addWidget(self.sliceCtrl)

        thickBox = QHBoxLayout()
        thickBox.addWidget(self.thickLabel)
        thickBox.addWidget(self.thickCtrl)

        sp_box = QHBoxLayout()
        sp_box.addLayout(sliceBox)
        sp_box.addLayout(thickBox)
        return sp_box

    def doLayout(self):
        winBox = QHBoxLayout()
        winBox.addWidget(self.winLabel)
        winBox.addWidget(self.windowCtrl)

        levBox = QHBoxLayout()
        levBox.addWidget(self.levLabel)
        levBox.addWidget(self.levelCtrl)

        wl_box = QHBoxLayout()
        wl_box.addLayout(winBox)
        wl_box.addLayout(levBox)

        view_box = QHBoxLayout()
        view_box.addWidget(self.imageReport)

        vBox = QVBoxLayout()
        vBox.addLayout(wl_box)
        if(self.vi.mode == 3 ):
            vBox.addLayout(self.doLayout3D())
        vBox.addLayout(view_box)

        central_widget = QWidget()
        central_widget.setLayout(vBox)
        self.setCentralWidget(central_widget)

        #self.setGeometry(100,100,self.slice.shape[1],self.slice.shape[0])
        self.setWindowTitle("test")

    #function added by Jeannie
    def closeEvent(self, event):
        self.Save()
        self.writeCriticalPoints()
        event.accept()

    #added by Jeannie
    def Save(self):
        doc = self.commentEditor.document()
        block = doc.begin()
        lines = [ block.text() ]
        for i in range( 1, doc.blockCount() ):
            block = block.next()
            lines.append( block.text() )
        comment=""	
        for item in lines:
            comment=comment+ str(item)+"\n"
        #print "comment", comment
        if( comment ):
            f = open(self.commentsFile, 'w')
            f.write(comment)
            f.close()
    
    def createAnnoMenu(self):
        try:
            # create Anno menu
            annoMenu = self.menuBar().addMenu("&Annotation")
            pixmap = QPixmap(22,22)
            setAction = True
            annoGroup = QActionGroup(annoMenu)

            for label in self.labelScheme.keys():
                txtLabel = "%s (%s)"%(label,self.labelScheme.get(label,"purple"))
                color = QColor(self.labelScheme.get(label,"purple"))
                pixmap.fill(color)
                action = annoMenu.addAction(QIcon(pixmap),label,self.setColor)
                action.setData(QVariant(color))
                action.setCheckable(True)
                action.setActionGroup(annoGroup)
                if( setAction):
                    if label == self.currentLabel:
                        action.setChecked(True)
                        setAction = False

        except Exception, error:
            print "failed in createAnnoMenu", error
    def createWinLevMenu(self):
        try:
            winlevMenu = self.menuBar().addMenu("&Window/Level")

            for label in self.winlevs.keys():
                action = winlevMenu.addAction(label,self.setPredefinedWinLev)

        except Exception, error:
            print "failed in createAnnoMenu", error
    def processChanges(self):
        self.vi.getData()
        self.vi.winlevelimage()
        self.DrawCritPoints()
        self.init_buffer()
    def setPredefinedWinLev(self):
        action = self.sender()
        label = str( action.text() )
        wl = self.winlevs[label]
        self.vi.setWindow(wl['win'])
        self.windowCtrl.setValue(wl['win'])
        self.vi.setLevel(wl['lev'])
        self.levelCtrl.setValue(wl['lev'])
        self.processChanges()
    def changeWindow(self):
        self.vi.setWindow(self.windowCtrl.value())
        self.processChanges()
    def changeLevel(self):
        self.vi.setLevel(self.levelCtrl.value())
        self.processChanges()

    def changeSlice(self):
        self.vi.setSlice(self.sliceCtrl.value())
        self.processChanges()
    def changeThick(self):
        self.vi.setPT(self.thickCtrl.value())
        self.processChanges()

    def setColor(self):
        action = self.sender()
        if( action is not None and isinstance(action,QAction)):
            color = QColor(action.data())
            #print action.text()
            self.currentLabel = str(action.text())
            #if( color.isValid() ):
                #print "set color to",color

    def createOrientMenu(self):

        orientMenu = self.menuBar().addMenu("&Orient")
        action = orientMenu.addAction("Flip LR",self.flipSliceLR)
        action = orientMenu.addAction("Flip TB",self.flipSliceUD)

    def createGlobalLocalMenu(self):
        
        globallocalMenu = self.menuBar().addMenu("&Global/Local")
        annoGroup = QActionGroup(globallocalMenu)
        action = globallocalMenu.addAction("Local View",self.setLocalView)
        action.setCheckable(True)
        action.setChecked(True)
        action.setActionGroup(annoGroup)
        action = globallocalMenu.addAction("Global View",self.setGlobalView)
        action.setCheckable(True)
        action.setActionGroup(annoGroup)
    def createProjectionMenu(self):
        projectionMenu = self.menuBar().addMenu("&Rendering")
        annoGroup = QActionGroup(projectionMenu)
        action = projectionMenu.addAction("maximum",self.setMaximumView)
        action.setCheckable(True)
        action.setChecked(True)
        action.setActionGroup(annoGroup)
        action = projectionMenu.addAction("minimum",self.setMinimumView)
        action.setCheckable(True)
        action.setActionGroup(annoGroup)
        action = projectionMenu.addAction("summation",self.setSummationView)
        action.setCheckable(True)
        action.setActionGroup(annoGroup)
    def flipSliceUD(self):
        browse.flipSliceUD(self)
        #print "call init_buffer from flipSliceUD"
        self.init_buffer()
    def flipSliceLR(self):
        browse.flipSliceLR(self)
        #print "call init_buffer from flipSliceLR"
        self.init_buffer()

    def createScaleMenu(self):
        scaleMenu = self.menuBar().addMenu("&Scale")
        action = scaleMenu.addAction("&Reset",self.ResetScale)
        action.setShortcut(QKeySequence("Ctrl+="))
        action = scaleMenu.addAction("&Increase",self.IncreaseScale)
        action.setShortcut(QKeySequence("Ctrl+I"))
        action = scaleMenu.addAction("&Decrease",self.DecreaseScale)
        action.setShortcut(QKeySequence("Ctrl+D"))

    def ResetScale(self):
        browse.ResetScale(self)
        self.init_buffer()
    def DecreaseScale(self):
        browse.DecreaseScale(self)
        self.init_buffer()
    def IncreaseScale(self):
        browse.IncreaseScale(self)
        self.init_buffer()
    def createViewMenu(self):
        viewMenu = self.menuBar().addMenu("&View")
        annoGroup = QActionGroup(viewMenu)

        action = viewMenu.addAction("&XY",self.SetViewXY)
        action.setCheckable(True)
        action.setChecked(True)
        action.setActionGroup(annoGroup)
        action.setShortcut(QKeySequence("Ctrl+z"))

        action = viewMenu.addAction("&XZ",self.SetViewXZ)
        action.setCheckable(True)
        action.setActionGroup(annoGroup)
        action.setShortcut(QKeySequence("Ctrl+y"))
       
        action = viewMenu.addAction("&YZ",self.SetViewYZ)
        action.setCheckable(True)
        action.setActionGroup(annoGroup)
        action.setShortcut(QKeySequence("Ctrl+x"))
    def SetViewXY(self):
        browse.SetViewXY(self)
        #self.sliceCtrl.SetRange(0,self.shape[0]-1)
        #self.sliceCtrl.SetValue(self.wslice)
        self.UpdateGeometryDefinitions()
        self.init_buffer()
    def SetViewXZ(self):
        browse.SetViewXZ(self)
        #self.sliceCtrl.SetRange(0,self.shape[1]-1)
        #self.sliceCtrl.SetValue(self.wslice)
        self.UpdateGeometryDefinitions()
        self.init_buffer()
    def SetViewYZ(self):
        browse.SetViewYZ(self)
        #self.sliceCtrl.SetRange(0,self.shape[2]-1)
        #self.sliceCtrl.SetValue(self.wslice)
        self.UpdateGeometryDefinitions()
        self.init_buffer()

    def setLocalView(self):
        self.vi.setViewType('local')
        self.processChanges()

    def setGlobalView(self):
        self.vi.setViewType('global')
        self.processChanges()

    def setMaximumView(self):
        self.vi.setProjection("max")
        self.processChanges()
    def setMinimumView(self):
        self.vi.setProjection("min")
        self.processChanges()
    def setSummationView(self):
        self.vi.setProjection("average")
        self.processChanges()

    def UpdateGeometryDefinitions(self):
        self.thickCtrl.setRange(0,(self.vi.getNumSlices()-1)/2)
        self.thickCtrl.setValue(self.vi.getPT())
        self.sliceCtrl.setRange(0,self.vi.getNumSlices()-1)
        self.sliceCtrl.setValue(self.vi.getSlice())
        #print "UpdateGeometryDefinitions"
    def drawCritPoints(self):
        try:
            #print "drawCritPoint", len(self.drawpoints)
            objects = []
            # get display matrix
            m = self.getDisplayScaling()
            for obj in self.critobjects:
                # there may be better ways to do this, but what I'm going to do
                # for now is return any portions of the
                obj.setIntersection(self.viewLow,self.viewHigh)
                p = obj.getDisplay(m,self.vi.cview)
                color = QColor(self.labelScheme.get(obj.getLabel(),"purple"))
                if (p.ndim == 1 ):
                    tmp1 = QPoint(p[0],p[1])
                    p1 = self.grview.mapToScene(tmp1)
                    p2 = QPointF(self.grview.mapFromScene(p1))
                    objects.append((p2,color))
                else:
                    seg = QPolygonF()
                    
                    for i in range(p.shape[0]):
                        tmp1 = QPoint(p[i,0],p[i,1])
                        p1 = self.grview.mapToScene(tmp1)
                        p2 = QPointF(self.grview.mapFromScene(p1))
                        seg.append(p2)
                    objects.append((seg,color))

            self.scene.addItem(CritPointsItem(objects,self.getDisplayScaling()))

            self.update()
        except Exception, error:
            print "failed in drawCritPoints", error
    def init_buffer(self):
        rawbuffer = self.vi.getRawBuffer()
        h,w,depth = rawbuffer.shape
        image = qn.rgb2qimage(rawbuffer)
        scls = self.vi.getScale()
        try:
            self.pix.fill(QColor(255,255,255))
        except Exception, error:
            pass

        self.pix = QPixmap.fromImage(image)
        
        sz = self.pix.size()
        self.scaling = [self.scale*scls[0],self.scale*scls[1]]
        newsz = [sz.width()*self.scaling[0],sz.height()*self.scaling[1]]
        if( newsz[0] != sz.width() or newsz[1] != sz.height()):
            self.pix = self.pix.scaled(newsz[0],newsz[1])
        # Empty out the scene. Think of a better way
        #print "scene.clear()"
        self.scene.clear()
        #print "addPixmap"
        self.scene.addPixmap(self.pix)
        #self.scene.addPixmap(QPixmap('DSC_2201.jpg'))
        #print "grview.show()"
        
        #print "drawCritPoints"
        self.drawCritPoints()
        #print "init_buffer"
        self.grview.show()
        QCoreApplication.flush()

    def getPoint(self, pos):
        if( pos.x() < 0 or pos.y() < 0 ):
            return None
        x = pos.x() ; y = pos.y()
        crds = self.mapFromDisplay([x,y])     
        p = CritPoint()
        p.extend(crds)
        p.setLabel(self.currentLabel)
        #print "getPoint",p.getLabel()
        return p 
    def processImageClick(self, vals):
        try:
            #print "processImageClick"
            #print vals
            pos = vals['pos']
            p = self.getPoint(pos)
            if( not vals['shift'] and not vals['ctrl'] ):
                #print p, self.vi.getValue(p)
                return
            if( vals['shift'] and not vals['ctrl'] and p):
                self.critpoints.append(p)
            elif( vals['ctrl'] and not vals['shift'] and p):
                self.modifyNearestCritPoint(p)
            elif( vals['ctrl'] and vals['shift'] and p):
                self.deleteNearestCritPoint(p)
            self.writeCriticalPoints()
            #print self.critpoints
            self.DrawCritPoints()
            #print self.drawpoints
            self.init_buffer()
        except Exception, error:
            print "failed in processImageClick", error

def call( data=None, **args ):
    labelFile = args.get('labelFile', None)
    if( labelFile ):
        labels = getLabels(labelFile)
    else:
        labels = []
    colorFile = args.get('colorFile', None)
    if( colorFile ):
        colors = getColors(colorFile)
    else:
        colors = []
    winlevFile = args.get("winlevFile",None)
    if( winlevFile ):
        winlevs = getWinLevelSettings(winlevFile)
    else:
        winlevs = {}
    labelScheme = getLabelScheme(labels,colors)
    app = QApplication(sys.argv)
    b = browseqt(None,data=data, labelScheme = labelScheme,
                 winlevs = winlevs, defaultLabel = labels[0], **args)
    b.show()
    sys.exit(app.exec_())
